package com.spbsu.search.stemmer.impl;

import com.spbsu.search.stemmer.Stemmer;
import sun.io.ByteToCharCp1251;
import java.io.*;
import java.util.ArrayList;

public class Stemka implements Stemmer {
    private static final char[] vowels = {'а', 'е', 'и', 'о', 'у', 'ы', 'э', 'ю', 'я'};
    private short[] fuzzy;
    private char[] fuzzychar;
    private int minstem;
    private int mcount;
    private boolean loadfuzzy;
    private ArrayList<Integer> lplist;

    public Stemka (CharSequence lang, CharSequence path){
        lplist  = new ArrayList<Integer>();
        loadfuzzy = false;
        try{
            if (!loadFuzzy(lang, path)){
                throw new IOException("Не удалось прочитать словарь суффиксов");
            }
            else{
                loadfuzzy = true;
            }
        }
        catch (IOException ioex){
            System.out.println(ioex.getMessage());
        }
        catch (Exception ex){
            System.out.println(ex.getMessage());
        }
    }
    public CharSequence stem(CharSequence word){
        if (loadfuzzy){
            minstem = getMinStem(word);
            mcount = 5;
            lplist.clear();
            if (getStemLen(word, 0, word.length() - 1) > 0)
                return word.subSequence(0,lplist.get(0));
            else
                return word;
        }
        else {
            return word;
        }
    }
    private int getMinStem(CharSequence word){
        int len = word.length();
        for (int i = 0; i < len; i++){
            if (isVowel(word.charAt(i))){
                while(++i < len && isVowel(word.charAt(i))){
                }
                return i + 1;
            }
        }
        return len;
    }
    private int getStemLen(CharSequence word, int offset, int lptail){
        int cchars = fuzzy[offset];
        int pchars = offset + cchars;
        int lpoffs = (pchars - 1) + cchars*2;
        int result = 0;
        char ending;
        if (lptail < 0)
            ending = (char)0;
        else
            ending = word.charAt(lptail);
        while (pchars > offset){
            while (pchars > offset && fuzzy[pchars] != 0 && fuzzychar[pchars] != ending){
                pchars--;
                lpoffs -= 2;
            }
            if (pchars <= offset)
                continue;
            if (fuzzychar[pchars] == ending && lptail + 1 > minstem - 2){
                int lpoffs_tmp = (fuzzy[lpoffs + 1] << 8) + fuzzy[lpoffs];
                result += getStemLen(word, lpoffs_tmp << 3, lptail - 1);
            }
            else{
                if (fuzzy[pchars]==0 && lptail + 1 >= minstem - 2 && mcount != 0){
                    lplist.add(lptail + 3);
                    mcount--;
                    return result + 1;
                }
            }
            pchars--;
            lpoffs -= 2;
        }
        return result;
    }
    private boolean loadFuzzy(CharSequence lang, CharSequence path) throws IOException {
        File fuz = new File(path + "fuzzy" + lang + ".dat");
        if (fuz.exists() && fuz.isFile()){
            FileInputStream fileInputStream = new FileInputStream(fuz);
            int length = (int)fuz.length();
            byte[] bytes = new byte[length];
            int offset = 0,numRead;
            while (offset < length && (numRead = fileInputStream.read(bytes, offset, length - offset)) >= 0){
                offset += numRead;
            }
            if (offset  < length){
                throw new IOException("Не удается прочесть файл до конца (прочитано " + offset + " байт из " + length); 
            }
            fileInputStream.close();
            fuzzy = new short[length];
            fuzzychar = new char[length];
            ByteToCharCp1251 byteToCharCp1251 = new ByteToCharCp1251();
            fuzzychar = byteToCharCp1251.convertAll(bytes);
            for (int j = 0; j < length; j++){
                fuzzy[j] = (short)(bytes[j]&0x000000FF);
            }
            return true;
        }
        else{
            throw new IOException("Не удается найти словарь суффиксов");
        }
    }
    private boolean isVowel(char letter){
        for (int i =0; i < 9; i++){
            if (letter == vowels[i])
                return true;
        }
        return false;
    }
}
